package source.model;

import source.model.type.*;
import source.model.UnitMission;
import source.events.action.Action;
import source.events.action.BaseNewTurn;
import java.util.*;

public class Base extends Entity{

	private ObjectID planetOnID;
	private final GameObjectType got = GameObjectType.BASE;
	private static int nextId = 0;
	private ArrayList<AddOnType> ao;
	private Hashtable<Unit, Float> healingUnits;
	private static final int oreExtractionRate = 999;
	private LinkedList<UnitType> baseQueue;
	private final StructureType sType = StructureType.BASE;
	private Health health;
	private boolean hasRallyPoint;
	private UnitMission rallyPoint;
	
	private int mpLeft;  // the remaining manpower needed to build the current Unit
	
	public Base(ObjectID planetOnID){
		super(GameObjectType.BASE, nextId++);
		this.planetOnID=planetOnID;
		baseQueue = new LinkedList<UnitType>();
		health = new Health(sType.health());
		healingUnits = new Hashtable<Unit, Float>();
		hasRallyPoint = false;
	}
	
	
	public ObjectID planetID(){
		return planetOnID;
	}
	
	public boolean isPlayerOwned() {
		return true;
	}

	public StructureType getType(){
		return sType;
	}	
	
	public void transferResources(){
		
	}
	/**
	 * This method is used to mark the object as having been forcibly moved (i.e.,
	 * Unit teleported using Area Effect) and that it's possition needs to be changed.
	 * @param x new x-coordinate
	 * @param y new y-coordinate
	 * @return true if object allowed the forced change, false otherwise
	 */
	public boolean changePosition( int x, int y ){
		//TODO:  implement method
		return true;
	}

	public boolean isRallyPointSet(){
		return hasRallyPoint;
	}

	public UnitMission getRallyPoint(){
		return rallyPoint;
	}
	
	public void addRallyPoint(UnitMission newRallyPoint){
		rallyPoint = newRallyPoint;
		hasRallyPoint = true;
	}

	/**
	 * Queries the object to find out if the changePositionFlag is set, meaning
	 * the object's position in pMap must be updated
	 * @return true if flag set; false otherwise
	 */
	public boolean changePositionFlag(){
		//TODO:  implement method
		return false;
	}
	
	public Position newPosition(){
		//TODO: implement method
		return new Position(0, 0);
	}
	
	/**
	 * Once the DataHandler resets the position of the object, it calls this method to
	 * let the object know that it is now no longer in a "Forced Position Change" 
	 * state
	 * @return true if the object reset it's flag, false otherwise
	 */
	public boolean resetPositionChangedFlag(){
		//TODO:  implement method
		return true;
		
	}
	
	public Action newTurn()
	{
		return new BaseNewTurn(objectID());
	}
	
	public void addMission(UnitType unit) {
		baseQueue.add(unit);
	}
	
	public UnitType nextMission() {
		if (baseQueue.size() == 0) {
			return null;
		}
		else {
			return baseQueue.removeFirst();
		}
	}
	
	public void removeLastMission() {
		baseQueue.removeLast();
	}
	
	public boolean emptyMission() {
		if (baseQueue.size() == 0) {
			return true;
		}
		else { 
			return false;
		}
	}
	
	public UnitType peekMission() {
		if (baseQueue.size() == 0) {
			return null;
		}
		else {
			return baseQueue.peek();
		}
	}
	
	/**
	 * Adds a unit to the base's "dry dock" (obviously where repairs are made
	 * Once in drydock, a unit cannot move out of the base unitl it is totally
	 * repaired. 
	 * 
	 * @param u - unit to be added to the healing queue
	 * @param healingAmountPerTurn - the amount the unit will be healed each
	 * turn until it is at full health.
	 */
	public void addUnitToHeal(Unit u, float healingAmountPerTurn)
	{
		healingUnits.put(u, new Float(healingAmountPerTurn));
		
		u.clearMovement();
	}
	
	/**
	 * The base will preform repairs on all units in its docks.  Once repairs
	 * are complete, the unit is removed from dock and will be usable next
	 * turn.  The return array gives all units that were removed from the dock
	 * because of this call. this can only be done once per turn.
	 * 
	 * @return An ArrayList containing all units that finished healing that turn
	 */
	public ArrayList<Unit> healUnits()
	{
		float healingAmount;
		ArrayList<Unit> finishedUnits = new ArrayList<Unit>();
		
		// cycle through the units in the healing set
		for(Unit current : healingUnits.keySet())
		{
			healingAmount = healingUnits.get(current).floatValue(); 
			
			// account for rounding error
			if(healingAmount - (int)healingAmount >= .5) 
				current.modHealth((int)healingAmount + 1);
			else
				current.modHealth((int)healingAmount);
			
			// make immobile
			current.clearMovement();
			
			if(current.currentHealth() == current.maxHealth())
				finishedUnits.add(current);
		}
		
		// remove the finihed units from the healing set
		for(Unit current : finishedUnits)
		{
			healingUnits.remove(current);
		}
		
		return finishedUnits;
	}
	
	public void setMPleft(int mpLeft) {
		this.mpLeft = mpLeft;
	}
	
	public int getMPleft() {
		return mpLeft;
	}
	
	public void decrementMPleft(int mpLeft) {
		this.mpLeft -= mpLeft;
	}
	
	/**
	 * WARNING: Base's as of this moment, when Joe and I go in
	 * and cut it open and do what we want with it, has a 
	 * static upkeep value. THIS CAN CHANGE LATER!!! I RECOMMEND
	 * IT CHANGE LATER!!!
	 * Something to think about...
	 */
	public int getUpkeep() {
		return sType.upkeep();
	}
	
	/**
	 * This is a useful comment.
	 */
	public int getExtractionRate()
	{
		return oreExtractionRate;
	}
	
	public int armor(){
		return sType.armor();
	}
	
	public int oDamage(){
		return sType.oDamage();
	}
	
	public int dDamage(){
		return sType.dDamage();
	}
	
	public int currentHealth(){
		return health.currentHealth();
	}
	
	public int maxHealth(){
		return health.maxHealth();
	}
	
	/**
	 * 
	 * @param modAmmount
	 * @return
	 * Returns true if alive, else returns false
	 */
	public boolean modHealth(int modAmmount){
		boolean alive = health.modHealth(modAmmount);
		if (!alive){
			invalidate();
		}
		return alive;
	}
	
	public java.util.Hashtable<String, String> getViewData()
	{
		java.util.Hashtable<String, String> viewData = super.getViewData();
		
		viewData.put("Structure Type", sType.getName());
		viewData.put("Maximum Health", Integer.toString(this.maxHealth()));
		viewData.put("Current Health", Integer.toString(this.currentHealth()));
		viewData.put("Production Rate", Integer.toString(sType.pRate()));
		
		String buildQueue = "";
		for(UnitType unit : baseQueue)
		{
			buildQueue += unit.getName() + ", ";
		}
		
		if(buildQueue != "") buildQueue = buildQueue.substring(0, buildQueue.length() - 2);
		else buildQueue = "none";
		
		viewData.put("Build Queue", buildQueue);
		
		String hUnits = "";
		for(Unit unit : healingUnits.keySet())
		{
			hUnits += unit.getName() + ", ";
		}
		
		if(hUnits != "") hUnits = hUnits.substring(0, hUnits.length() - 2);
		else hUnits = "none";
		
		viewData.put("Healing Units", hUnits);
		
		return viewData;
	}

}
